package com.mstarc.app.mnfc;

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.view.View;
import android.widget.Button;

import com.mstarc.app.mnfc.mstarcApiUtils.Conversion;
import com.mstarc.app.mnfc.mstarcApiUtils.InitializeUtil;
import com.mstarc.app.mnfc.mstarcApiUtils.WriteSEIDUtil;
import com.mstarc.nfc.Sle97;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileReader;
import java.io.IOException;
import java.security.spec.AlgorithmParameterSpec;
import java.util.Random;

import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

import mstarc_os_api.mstarc_os_api_msg;

/**
 * Created by qdyouke01 on 2017/7/3.
 */

public class WriteApduActivity extends Activity implements View.OnClickListener {

    public Sle97 mm;
    long mdev;

    boolean debug = false;
    String tag = "yhy";

    private final int MSG_XX = 2;
    long inc = 0;
    int para = 0;
    int[] test_buff = new int[]{/*0x01, 0xc0, 0x00, 0x00, 0x01*/0x00, 0xA4, 0x04, 0x00, 0x00};
    int[] test_buff1 = new int[]{0x00, 0xA4, 0x04, 0x00, 0x75};
    int[] get_seid = new int[]{0x80, 0x0C, 0xA0, 0x044, 0x00};
    int[] buff_getresponse = new int[]{0x00, 0xC0, 0x00, 0x00, 0x75};

    int[] buff_install_update = new int[]{0x80, 0x50, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,};

    int[] host_challenge = new int[8];

    int cap_file_lines = 0;

    Random mrandom = new Random(0);

    private mstarc_os_api_msg m_api_msg;
    private Button writeButton;
    int[] seid_buff = new int[]{0x17, 0x00, 0x30, 0x77, 0x04, 0x01, 0x47, 0x35, 0x83, 0x11};
    public Handler mhandler = new Handler() {
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case MSG_XX:
                    Log.i("yhy", "handleMessage:MSG_XX");
                    test_func1();
                    Message mssg = mhandler.obtainMessage();
                    mssg.what = MSG_XX;
                    //mhandler.sendMessageDelayed(mssg, 5000);
                    break;
                default:
                    break;


            }
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_write_apdu);
        initView();
        int[] buff = new int[]{0x00, 0x84, 0x00, 0x00, 0x08};
        //int[] buff = new int[]{0x00, 0xA4, 0x00, 0x00, 0x02, 0x3F, 0x00};
        //int[] buff = new int[]{0x00, 0xA4, 0x04, 0x00, 0x00};
        int[] buff1 = new int[]{0x00, 0xC0, 0x00, 0x00, 0x10};
        int[] response = new int[]{};
        int ret;
        int i;

		/*
        mm = new Sle97();
		mdev = mm.get_sle97();

		Sle97.sle97_open(mdev);

		ret = Sle97.sle97_ioctl(mdev, Sle97.com_7816cardrst, buff, buff.length);
		Log.i("yhy", "Sle97.com_7816cardrst status:" + ret);
		*/
        m_api_msg = InitializeUtil.getInstance(WriteApduActivity.this).getMstarc_os_api_msg();


    }

    private void initView() {
        writeButton = (Button) findViewById(R.id.activity_write_apdu_write_btn);
        writeButton.setOnClickListener(this);
    }

    @Override
    protected void onResume() {
        super.onResume();
        m_api_msg.mstarc_sle97_close();
    }

/*
    public void test_func()
	{
		int[] response = new int[]{};
		int ret;
		int i;

    	if(para < 0xff)
    	{
    		para += 1;
    	}
    	else
    	{
    		para = 0;
    	}

    	//test_buff[4] = para;

    	ret = Sle97.sle97_ioctl(mdev, Sle97.com_7816apdu, test_buff, test_buff.length);
		Log.i("yhy", "Sle97.com_7816apdu status:" + ret);
		response = Sle97.sle97_getresponce(mdev);

		for(i=0;i<response.length;i++)
		{
			Log.i("yhy", "response[" + i + "] = 0x" + Integer.toHexString(response[i]));
		}

		if(response.length >= 2)
		{
			if(response[response.length - 2] != 0x90 || response[response.length - 1] != 0x00)
			{
				Log.i("yhy", "APDU ERROR during " + inc + "times");
				inc = 0;
	//			Sle97.sle97_ioctl(mdev, Sle97.com_7816cardrst, test_buff, test_buff.length);
			}
			else
			{
				Log.i("yhy", "APDU succeed for " + inc + "times");
			}
		}

	}

*/

    public void test_func1() {
        int[] response;
        int ret;
        String cap_file = "/sdcard/szt_cap.apdu";


        m_api_msg.mstarc_sle97_init();
        m_api_msg.mstarc_sle97_reset();


        ret = select();

/*
        response = read_seid();

		if(response != null)
			for(int i=0;i<response.length;i++)
				Log.i("yhy", "seid["+ i + "] = " + Integer.toHexString(response[i]));
*/

        cap_file_lines = caculate_cap_file_line(cap_file);
        Log.i("yhy", "cap file lines = " + cap_file_lines);

        ret = auth();

        WriteSEIDUtil.write_seid(m_api_msg, seid_buff);

//        ret = write_cap_in_apdu(cap_file);
        Log.i("yhy", "write cap ret = " + ret);

        if (ret == cap_file_lines)
            Log.i("yhy", "succeed in write cap");
    }

    public int select() {
        int ret = 0;
        int i;
        int[] response = new int[]{};

        int[] command_select = new int[]{0x00, 0xA4, 0x04, 0x00, 0x00};

        Log.i("yhy", "APDU SELECT");

        response = m_api_msg.mstarc_sle97_apdu(command_select);
        if (response == null) {
            Log.i("yhy", "APDU SELECT: ERROR OCCURRED");
            return -1;
        }

        if (response.length >= 2) {
            if (response[response.length - 2] == 0x90 && response[response.length - 1] == 0x00) {
                return 0;
            } else if (response[response.length - 2] == 0x6C) {
                command_select[command_select.length - 1] = response[response.length - 1];

                response = m_api_msg.mstarc_sle97_apdu(command_select);

            }

            for (i = 0; i < response.length; i++) {
                Log.i("yhy", "response[" + i + "] = 0x" + Integer.toHexString(response[i]));
            }
        }

        return ret;
    }

    byte[] ENC_KEY = new byte[]{0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F};

    int[] key_div_data = new int[10];
    int[] key_info = new int[2];
    int[] seq_counter = new int[2];
    int[] card_challenge = new int[6];
    int[] card_cryptogram = new int[8];

    byte[] SENC_SESSION_KEY = new byte[16];
    byte[] CMAC_SESSION_KEY = new byte[16];

    byte[] host_cryptogram = new byte[8];
    byte[] c_mac = new byte[8];


    public int auth() {
        int i = 0;
        int ret = 0;
        int[] response;

        initialize_update();

        byte[] tmp = get_host_cryptogram();

        if (tmp.length == host_cryptogram.length)
            System.arraycopy(tmp, 0, host_cryptogram, 0, host_cryptogram.length);
        else if (tmp.length == 24)
            System.arraycopy(tmp, 16, host_cryptogram, 0, host_cryptogram.length);
        else
            Log.e("yhy", "get host cryptogram failed");

        if (debug) {
            for (byte x : host_cryptogram)
                Log.i("yhy", "host_cryptogram[" + i++ + "] = " + Integer.toHexString(x & 0xff));
        }
        c_mac = get_cmac();

        if (debug) {
            i = 0;
            for (byte x : c_mac)
                Log.i("yhy", "c_mac[" + i++ + "] = " + Integer.toHexString(x & 0xff));
        }

        int[] apdu_ext_auth = new int[5 + 8 + 8];

        apdu_ext_auth[0] = 0x84;
        apdu_ext_auth[1] = 0x82;
        apdu_ext_auth[2] = 0x00;
        apdu_ext_auth[3] = 0x00;
        apdu_ext_auth[4] = 0x10;

        for (i = 0; i < 8; i++)
            apdu_ext_auth[i + 5] = host_cryptogram[i];
        for (i = 0; i < 8; i++)
            apdu_ext_auth[i + 5 + 8] = c_mac[i];


        response = m_api_msg.mstarc_sle97_apdu(apdu_ext_auth);
        if (response.length >= 2) {
            if (response[response.length - 2] == 0x90 && response[response.length - 1] == 0x00) {
                ret = 0;
                Log.i("yhy", "sle97 auth succeed");
            } else {
                ret = 1;
            }

            if (debug) {
                for (i = 0; i < response.length; i++) {
                    Log.i("yhy", "response[" + i + "] = 0x" + Integer.toHexString(response[i]));
                }
            }
        }

        return ret;

    }


    public void generate_host_challenge() {
        int i = 0;

        for (i = 0; i < 8; i++) {
            host_challenge[i] = mrandom.nextInt(256);
            //Log.i("yhy", "host_challenge[" + i + "] = 0x" + Integer.toHexString(host_challenge[i]));
        }
    }

    public int initialize_update() {
        int i = 0;
        int[] response = new int[]{};
        int ret = 0;

        int[] apdu_command_header = new int[]{0x80, 0x50, 0x00, 0x00, 0x08};
        int[] command = new int[apdu_command_header.length + host_challenge.length];
        int[] apdu_get_response = new int[]{0x00, 0xC0, 0x00, 0x00, 0x00};

        Log.i("yhy", "initialize_update");

        generate_host_challenge();

        for (i = 0; i < apdu_command_header.length; i++)
            command[i] = apdu_command_header[i];
        for (i = apdu_command_header.length; i < command.length; i++)
            command[i] = host_challenge[i - apdu_command_header.length];

        if (debug) {
            for (i = 0; i < command.length; i++) {
                Log.i("yhy", "initialize_update command[" + i + "] = 0x" + Integer.toHexString(command[i]));
            }
        }

        response = m_api_msg.mstarc_sle97_apdu(command);

        if (response.length >= 2) {
            if (response[response.length - 2] == 0x90 || response[response.length - 1] == 0x00) {
                Log.i("yhy", "succeed");
            } else if (response[response.length - 2] == 0x61) {
                Log.i("yhy", "need get response");

                apdu_get_response[apdu_get_response.length - 1] = response[response.length - 1];

                response = m_api_msg.mstarc_sle97_apdu(apdu_get_response);
                ret = 1;
            }
        } else {
            return -1;
        }

        if (response.length == 2 + key_div_data.length + key_info.length + seq_counter.length + card_challenge.length + card_cryptogram.length) {
            System.arraycopy(response, 0, key_div_data, 0, key_div_data.length);
            System.arraycopy(response, key_div_data.length, key_info, 0, key_info.length);
            System.arraycopy(response, key_div_data.length + key_info.length, seq_counter, 0, seq_counter.length);
            System.arraycopy(response, key_div_data.length + key_info.length + seq_counter.length, card_challenge, 0, card_challenge.length);
            System.arraycopy(response, key_div_data.length + key_info.length + seq_counter.length + card_challenge.length, card_cryptogram, 0, card_cryptogram.length);
        }

        if (debug) {
            for (i = 0; i < seq_counter.length; i++) {
                Log.i("yhy", "seq_counter[" + i + "] = 0x" + Integer.toHexString(seq_counter[i]));
            }

            for (i = 0; i < card_challenge.length; i++) {
                Log.i("yhy", "card_challenge[" + i + "] = 0x" + Integer.toHexString(card_challenge[i]));
            }

            for (i = 0; i < card_cryptogram.length; i++) {
                Log.i("yhy", "card_cryptogram[" + i + "] = 0x" + Integer.toHexString(card_cryptogram[i]));
            }
        }

        return ret;

    }

    public byte[] get_cmac_session_key() {
        int i = 0;
        byte[] DERIVATION_DATA = new byte[16];

        Log.i("yhy", "get_cmac_session_key");

        for (i = 0; i < 16; i++)
            DERIVATION_DATA[i] = 0;

        DERIVATION_DATA[0] = 0x01;
        DERIVATION_DATA[1] = 0x01;
        DERIVATION_DATA[2] = (byte) seq_counter[0];
        DERIVATION_DATA[3] = (byte) seq_counter[1];

        if (debug) {
            for (i = 0; i < DERIVATION_DATA.length; i++)
                Log.i("yhy", "DERIVATION_DATA[" + i + "] = " + Integer.toHexString(DERIVATION_DATA[i] & 0xff));
        }

        byte[] icv = new byte[]{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};

        return des3_encrypt(DERIVATION_DATA, ENC_KEY, icv);
    }


    public byte[] get_senc_session_key() {
        int i = 0;
        byte[] DERIVATION_DATA = new byte[16];

        Log.i("yhy", "get_senc_session_key");

        for (i = 0; i < DERIVATION_DATA.length; i++)
            DERIVATION_DATA[i] = 0;

        DERIVATION_DATA[0] = 0x01;
        DERIVATION_DATA[1] = (byte) 0x82;
        DERIVATION_DATA[2] = (byte) seq_counter[0];
        DERIVATION_DATA[3] = (byte) seq_counter[1];

        if (debug) {
            for (i = 0; i < DERIVATION_DATA.length; i++)
                Log.i("yhy", "DERIVATION_DATA[" + i + "] = " + Integer.toHexString(DERIVATION_DATA[i] & 0xff));
        }

        byte[] icv = new byte[]{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};

        return des3_encrypt(DERIVATION_DATA, ENC_KEY, icv);
    }

    public byte[] get_host_cryptogram() {
        int i = 0;
        byte[] DERIVATION_DATA = new byte[24];

        Log.i("yhy", "get_host_cryptogram");

        for (i = 0; i < DERIVATION_DATA.length; i++)
            DERIVATION_DATA[i] = 0;

        DERIVATION_DATA[0] = (byte) seq_counter[0];
        DERIVATION_DATA[1] = (byte) seq_counter[1];

        for (i = 0; i < card_challenge.length; i++)
            DERIVATION_DATA[i + 2] = (byte) card_challenge[i];

        for (i = 0; i < host_challenge.length; i++)
            DERIVATION_DATA[i + 2 + card_challenge.length] = (byte) host_challenge[i];

        DERIVATION_DATA[2 + card_challenge.length + host_challenge.length] = (byte) 0x80;

        if (debug) {
            for (i = 0; i < DERIVATION_DATA.length; i++)
                Log.i("yhy", "DERIVATION_DATA[" + i + "] = " + Integer.toHexString(DERIVATION_DATA[i] & 0xff));
        }

        byte[] icv = new byte[]{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
        byte[] senc_session_key = get_senc_session_key();

        if (debug) {
            for (i = 0; i < senc_session_key.length; i++)
                Log.i("yhy", "senc_session_key[" + i + "] = " + Integer.toHexString(senc_session_key[i] & 0xff));
        }

        return des3_encrypt(DERIVATION_DATA, senc_session_key, icv);
    }


    public byte[] des3_encrypt(byte[] data, byte[] key, byte[] icv) {

        try {
            SecretKey deskey = new SecretKeySpec(key, "desede");

            IvParameterSpec iv = new IvParameterSpec(icv);
            AlgorithmParameterSpec paramSpec = iv;

            Cipher cl = Cipher.getInstance("desede/CBC/NoPadding");
            cl.init(Cipher.ENCRYPT_MODE, deskey, paramSpec);

            return cl.doFinal(data);
        } catch (java.security.NoSuchAlgorithmException e1) {
            e1.printStackTrace();
        } catch (javax.crypto.NoSuchPaddingException e2) {
            e2.printStackTrace();
        } catch (java.lang.Exception e3) {
            e3.printStackTrace();
        }

        return null;

    }


    public byte[] des_encrypt(byte[] data, byte[] key, byte[] icv) {

        try {
            SecretKey deskey = new SecretKeySpec(key, "DES");

            IvParameterSpec iv = new IvParameterSpec(icv);
            AlgorithmParameterSpec paramSpec = iv;

            Cipher cl = Cipher.getInstance("DES/CBC/NoPadding");
            cl.init(Cipher.ENCRYPT_MODE, deskey, paramSpec);

            return cl.doFinal(data);
        } catch (java.security.NoSuchAlgorithmException e1) {
            e1.printStackTrace();
        } catch (javax.crypto.NoSuchPaddingException e2) {
            e2.printStackTrace();
        } catch (java.lang.Exception e3) {
            e3.printStackTrace();
        }

        return null;

    }

    public byte[] get_cmac() {
        int i = 0;
        byte[] DERIVATION_DATA = new byte[16];
        byte[] data_left_half = new byte[8];
        byte[] data_right_half = new byte[8];
        byte[] key_right_half = new byte[8];

        Log.i("yhy", "get_cmac");

        for (i = 0; i < DERIVATION_DATA.length; i++)
            DERIVATION_DATA[i] = 0;

        DERIVATION_DATA[0] = (byte) 0x84;
        DERIVATION_DATA[1] = (byte) 0x82;
        DERIVATION_DATA[2] = 0x00;
        DERIVATION_DATA[3] = 0x00;
        DERIVATION_DATA[4] = 0x10;

        for (i = 0; i < host_cryptogram.length; i++)
            DERIVATION_DATA[i + 5] = host_cryptogram[i];

        DERIVATION_DATA[5 + 8] = (byte) 0x80;

        System.arraycopy(DERIVATION_DATA, 0, data_left_half, 0, 8);
        System.arraycopy(DERIVATION_DATA, 8, data_right_half, 0, 8);

        if (debug) {
            for (i = 0; i < data_left_half.length; i++)
                Log.i("yhy", "left_half[" + i + "] = " + Integer.toHexString(data_left_half[i] & 0xff));
            for (i = 0; i < data_right_half.length; i++)
                Log.i("yhy", "right_half[" + i + "] = " + Integer.toHexString(data_right_half[i] & 0xff));
        }

        byte[] cmac_session_key = get_cmac_session_key();

        if (debug) {
            for (i = 0; i < cmac_session_key.length; i++)
                Log.i("yhy", "cmac_session_key[" + i + "] = " + Integer.toHexString(cmac_session_key[i] & 0xff));
        }

        System.arraycopy(cmac_session_key, 0, key_right_half, 0, key_right_half.length);

        byte[] icv = new byte[]{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};

        byte[] icv1 = des_encrypt(data_left_half, key_right_half, icv);

        return des3_encrypt(data_right_half, cmac_session_key, icv1);

    }

    public int caculate_cap_file_line(String path) {
        int lines = 0;

        try {

            File f = new File(path);

            FileInputStream fis = new FileInputStream(f);

            int f_size = fis.available();

            BufferedReader bfr = new BufferedReader(new FileReader(f), f_size);
            String line = bfr.readLine();

            while (line != null && line.length() != 0) {

                lines++;

                line = bfr.readLine();

            }
            bfr.close();

        } catch (IOException e) {
            e.printStackTrace();
        }

        return lines;
    }

    public int write_cap_in_apdu(String path) {

        int ret = 0;
        int dealed_lines = 0;
        int[] response;
        int[] apdu_get_response = new int[]{0x00, 0xC0, 0x00, 0x00, 0x00};
        byte[] tmp;
        int[] apdu;

        Log.i("yhy", "write_cap_in_apdu");

        try {

            File f = new File(path);

            FileInputStream fis = new FileInputStream(f);

            int f_size = fis.available();

            BufferedReader bfr = new BufferedReader(new FileReader(f), f_size);
            String line = bfr.readLine();

            while (line != null && line.length() != 0) {

                //cap_file_lines++;

                tmp = new byte[line.length() / 2];
                apdu = new int[line.length() / 2];

                Conversion.hexStringtoByte(line, tmp);

                for (int i = 0; i < tmp.length; i++)
                    apdu[i] = tmp[i] & 0xff;

                response = m_api_msg.mstarc_sle97_apdu(apdu);
                if (response.length >= 2) {
                    if (response[response.length - 2] == 0x90 && response[response.length - 1] == 0x00) {
                        ret = 0;
                        //Log.i("yhy", "succeed");
                    } else if (response[response.length - 2] == 0x61) {
                        Log.i("yhy", "need get response");

                        apdu_get_response[apdu_get_response.length - 1] = response[response.length - 1];

                        response = m_api_msg.mstarc_sle97_apdu(apdu_get_response);
                        ret = 1;

                        dealed_lines++;

                    } else {
                        ret = 2;
                        return ret;
                    }

                    if (debug) {
                        for (int i = 0; i < response.length; i++) {
                            Log.i("yhy", "response[" + i + "] = 0x" + Integer.toHexString(response[i]));
                        }
                    }
                }

                line = bfr.readLine();

            }
            bfr.close();

            //Log.d(tag, "write_cap_in_apdu: " + sb.toString());
        } catch (IOException e) {
            e.printStackTrace();
        }


        return dealed_lines;

    }


    public int[] read_seid() {
        int[] response;
        int[] seid = new int[12];
        int ret;

        int[] apdu_read_seid = new int[]{0x80, 0xCA, 0x00, 0x44, 0x00};
        //int[] apdu_get_response = new int[] {0x00, 0xC0, 0x00, 0x00, 0x00};

        Log.i("yhy", "read seid");

        response = m_api_msg.mstarc_sle97_apdu(apdu_read_seid);
        if (response.length >= 2) {
            if (response[response.length - 2] == 0x90 && response[response.length - 1] == 0x00) {
                ret = 0;
                Log.i("yhy", "write cap file succeed");
            } else if (response[response.length - 2] == 0x6c) {
                Log.i("yhy", "le error, redo with new le");

                apdu_read_seid[apdu_read_seid.length - 1] = response[response.length - 1];

                response = m_api_msg.mstarc_sle97_apdu(apdu_read_seid);

                //if(response.length>10)
                System.arraycopy(response, 0, seid, 0, response.length - 2);

                ret = 1;
            } else {
                ret = 2;
                return null;
            }

            if (debug) {
                for (int i = 0; i < response.length; i++) {
                    Log.i("yhy", "response[" + i + "] = 0x" + Integer.toHexString(response[i]));
                }
            }

        }

        return seid;

    }

    @Override
    public void onClick(View view) {
        switch (view.getId()) {
            case R.id.activity_write_apdu_write_btn:
                Message msg = mhandler.obtainMessage();
                msg.what = MSG_XX;
                mhandler.sendMessageDelayed(msg, 2000);
                break;
        }
    }
}
